#!/usr/bin/env python3
import os
import glob
import subprocess
import json
import csv
import xml.etree.ElementTree as ET
import time
import tarfile
from datetime import datetime
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

class AdvanceMAMEFrontend:
    def __init__(self):
        # 版本号
        self.version = "2.5"
        
        # 设置路径
        self.advmame_path = "/usr/local/bin/advmame"
        self.advmame_config_path = os.path.expanduser("~/.advance/advmame.rc")
        self.save_states_path = os.path.expanduser("~/.advance/sta")
        self.cache_dir = os.path.expanduser("~/.mamegui")
        self.cache_file = os.path.join(self.cache_dir, "game_aliases.json")
        self.recent_file = os.path.join(self.cache_dir, "recent_games.json")
        self.tags_file = os.path.join(self.cache_dir, "game_tags.json")
        self.custom_names_file = os.path.join(self.cache_dir, "custom_names.json")
        self.theme_pref_file = os.path.join(self.cache_dir, "theme_preference.json")
        self.settings_file = os.path.join(self.cache_dir, "settings.json")
        self.window_state_file = os.path.join(self.cache_dir, "window_state.json")
        self.max_recent = 10
        
        # 标记类型
        self.TAG_NONE = 0
        self.TAG_GAME = 1
        self.TAG_BIOS = 2
        self.TAG_UNPLAYABLE = 3
        
        # 主题类型
        self.THEME_AUTO = 0
        self.THEME_DARK = 1
        self.THEME_LIGHT = 2
        
        # 图形渲染参数
        self.rgb_effects = [
            "no", "triad3dot", "triadstrong3dot", "triad6dot", 
            "triadstrong6dot", "triad16dot", "triadstrong16d",
            "scan2horz", "scan3horz", "scan2vert", "scan3vert"
        ]
        
        self.initializing = True
        self.game_liststore = None
        
        # 初始化目录
        self._ensure_cache_dir()
        self._ensure_save_states_dir()
        self._ensure_advmame_config_dir()
        
        # 加载设置
        self.settings = self._load_settings()
        # 从设置中获取ROM路径，默认使用旧路径
        self.rom_path = self.settings.get("rom_path", "/opt/advancemame/share/advance/rom")
        
        self.theme_preference = self._load_theme_preference()
        self._determine_theme()
        self._set_tag_colors_based_on_theme()
        
        # 创建UI
        self.create_main_window()
        self.game_aliases = self._load_game_aliases()
        self.recent_games = self._load_recent_games()
        self.game_tags = self._load_game_tags()
        self.custom_names = self._load_custom_names()
        self.create_ui_elements()
        
        # 加载列表
        self.refresh_rom_list()
        self.refresh_recent_list()
        
        self.initializing = False
        self.window.show_all()
    
    def _load_settings(self):
        """加载应用设置"""
        default_settings = {
            "show_bios_in_list": True,
            "show_unplayable_in_list": True,
            "rom_path": "/opt/advancemame/share/advance/rom"  # 默认ROM路径
        }
        
        if os.path.exists(self.settings_file):
            try:
                with open(self.settings_file, 'r', encoding='utf-8') as f:
                    saved_settings = json.load(f)
                    return {**default_settings,** saved_settings}
            except (json.JSONDecodeError, IOError) as e:
                print(f"加载设置失败: {e}")
                if os.path.exists(self.settings_file):
                    os.remove(self.settings_file)
        
        return default_settings
    
    def _save_settings(self):
        """保存应用设置"""
        try:
            with open(self.settings_file, 'w', encoding='utf-8') as f:
                json.dump(self.settings, f, ensure_ascii=False, indent=2)
            return True
        except IOError as e:
            print(f"保存设置失败: {e}")
            return False
    
    def _browse_rom_folder(self, entry):
        """浏览选择ROM文件夹"""
        dialog = Gtk.FileChooserDialog(
            title="选择ROM文件夹",
            parent=self.window,
            action=Gtk.FileChooserAction.SELECT_FOLDER
        )
        dialog.add_buttons(
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_OPEN, Gtk.ResponseType.OK
        )
        
        # 设置当前文件夹
        current_path = entry.get_text()
        if os.path.exists(current_path) and os.path.isdir(current_path):
            dialog.set_current_folder(current_path)
        
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            selected_path = dialog.get_filename()
            entry.set_text(selected_path)
            # 更新ROM路径并保存设置
            self.rom_path = selected_path
            self.settings["rom_path"] = selected_path
            self._save_settings()
            # 刷新游戏列表
            self.refresh_rom_list()
        
        dialog.destroy()
    
    def open_settings_window(self, widget):
        """打开设置窗口"""
        settings_window = Gtk.Window(title="设置")
        settings_window.set_default_size(600, 400)
        settings_window.set_transient_for(self.window)
        settings_window.set_modal(True)
        
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox.set_border_width(10)
        settings_window.add(vbox)
        
        notebook = Gtk.Notebook()
        vbox.pack_start(notebook, True, True, 0)
        
        # 常规设置
        general_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15)
        general_box.set_border_width(20)
        
        # 显示BIOS选项
        bios_checkbox = Gtk.CheckButton(label="在游戏列表显示BIOS文件")
        bios_checkbox.set_active(self.settings.get("show_bios_in_list", True))
        bios_checkbox.connect("toggled", self.on_bios_checkbox_toggled)
        general_box.pack_start(bios_checkbox, False, False, 0)
        
        # 显示不可玩游戏选项
        unplayable_checkbox = Gtk.CheckButton(label="在游戏列表显示不可玩游戏")
        unplayable_checkbox.set_active(self.settings.get("show_unplayable_in_list", True))
        unplayable_checkbox.connect("toggled", self.on_unplayable_checkbox_toggled)
        general_box.pack_start(unplayable_checkbox, False, False, 0)
        
        general_label = Gtk.Label(label="常规")
        notebook.append_page(general_box, general_label)
        
        # ROM文件夹设置
        rom_folder_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        
        rom_label = Gtk.Label(label="ROM文件夹:")
        rom_label.set_property("xalign", 0)
        rom_label.set_width_chars(15)
        rom_folder_box.pack_start(rom_label, False, False, 0)
        
        self.rom_path_entry = Gtk.Entry()
        self.rom_path_entry.set_text(self.rom_path)
        self.rom_path_entry.set_hexpand(True)
        rom_folder_box.pack_start(self.rom_path_entry, True, True, 0)
        
        browse_button = Gtk.Button(label="浏览...")
        browse_button.connect("clicked", lambda w: self._browse_rom_folder(self.rom_path_entry))
        rom_folder_box.pack_start(browse_button, False, False, 0)
        
        general_box.pack_start(rom_folder_box, False, False, 0)
        
        # 图形渲染设置
        render_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15)
        render_box.set_border_width(20)
        
        render_label = Gtk.Label(label="选择图形渲染效果:")
        render_label.set_property("xalign", 0)
        render_box.pack_start(render_label, False, False, 0)
        
        current_config = self._load_advmame_config()
        current_effect = current_config.get('display_rgbeffect', 'no')
        if current_effect not in self.rgb_effects:
            current_effect = 'no'
        
        columns_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        render_box.pack_start(columns_box, False, False, 0)
        
        column1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        columns_box.pack_start(column1, True, True, 0)
        
        column2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        columns_box.pack_start(column2, True, True, 0)
        
        first_button = None
        for i, effect in enumerate(self.rgb_effects):
            if first_button is None:
                btn = Gtk.RadioButton.new_with_label(None, effect)
                first_button = btn
            else:
                btn = Gtk.RadioButton.new_with_label_from_widget(first_button, effect)
            
            btn.set_property("xalign", 0)
            if effect == current_effect:
                btn.set_active(True)
            btn.connect("toggled", self._on_rgb_effect_toggled, effect)
            
            if i < 6:
                column1.pack_start(btn, False, False, 0)
            else:
                column2.pack_start(btn, False, False, 0)
        
        render_tab_label = Gtk.Label(label="图形渲染")
        notebook.append_page(render_box, render_tab_label)
        
        # 统计信息
        stats_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15)
        stats_box.set_border_width(20)
        
        stats_grid = Gtk.Grid()
        stats_grid.set_row_spacing(10)
        stats_grid.set_column_spacing(20)
        stats_box.pack_start(stats_grid, False, False, 0)
        
        rom_total, bios_count, game_count, unplayable_count = self.calculate_stats()
        row = 0
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>ROM总数:</b>")
        stats_grid.attach(label, 0, row, 1, 1)
        
        value_label = Gtk.Label(label=str(rom_total))
        value_label.set_property("xalign", 0)
        stats_grid.attach(value_label, 1, row, 1, 1)
        row += 1
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>BIOS总数:</b>")
        stats_grid.attach(label, 0, row, 1, 1)
        
        value_label = Gtk.Label(label=str(bios_count))
        value_label.set_property("xalign", 0)
        stats_grid.attach(value_label, 1, row, 1, 1)
        row += 1
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>可玩游戏总数:</b>")
        stats_grid.attach(label, 0, row, 1, 1)
        
        value_label = Gtk.Label(label=str(game_count))
        value_label.set_property("xalign", 0)
        stats_grid.attach(value_label, 1, row, 1, 1)
        row += 1
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>不可玩总数:</b>")
        stats_grid.attach(label, 0, row, 1, 1)
        
        value_label = Gtk.Label(label=str(unplayable_count))
        value_label.set_property("xalign", 0)
        stats_grid.attach(value_label, 1, row, 1, 1)
        row += 1
        
        buttons_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        stats_box.pack_start(buttons_box, False, False, 10)
        
        refresh_button = Gtk.Button(label="刷新统计数据")
        refresh_button.connect("clicked", self.refresh_stats, stats_grid)
        buttons_box.pack_start(refresh_button, False, False, 0)
        
        export_button = Gtk.Button(label="导出游戏列表")
        export_button.connect("clicked", lambda w: self.export_game_list(settings_window))
        buttons_box.pack_start(export_button, False, False, 0)
        
        stats_label = Gtk.Label(label="统计")
        notebook.append_page(stats_box, stats_label)
        
        close_button = Gtk.Button(label="关闭")
        close_button.set_hexpand(False)
        close_button.set_halign(Gtk.Align.CENTER)
        close_button.connect("clicked", lambda w: settings_window.destroy())
        vbox.pack_start(close_button, False, False, 0)
        
        settings_window.show_all()
    
    def export_game_list(self, parent_window):
        """导出游戏列表为CSV格式"""
        dialog = Gtk.FileChooserDialog(
            title="导出游戏列表",
            parent=parent_window,
            action=Gtk.FileChooserAction.SAVE
        )
        dialog.add_buttons(
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_SAVE, Gtk.ResponseType.OK
        )
        
        date_str = datetime.now().strftime("%Y%m%d_%H%M%S")
        default_filename = f"mame_game_list_{date_str}.csv"
        dialog.set_current_name(default_filename)
        
        filter_csv = Gtk.FileFilter()
        filter_csv.set_name("CSV 文件")
        filter_csv.add_pattern("*.csv")
        dialog.add_filter(filter_csv)
        
        response = dialog.run()
        
        if response == Gtk.ResponseType.OK:
            file_path = dialog.get_filename()
            if not file_path.endswith(".csv"):
                file_path += ".csv"
            
            try:
                game_list = []
                rom_files = glob.glob(os.path.join(self.rom_path, "*.zip"))
                
                for rom_file in rom_files:
                    rom_basename = os.path.basename(rom_file)
                    rom_name = os.path.splitext(rom_basename)[0]
                    
                    tag_type = self.game_tags.get(rom_name, self.TAG_NONE)
                    
                    if not self.settings.get("show_bios_in_list", True) and tag_type == self.TAG_BIOS:
                        continue
                    
                    if not self.settings.get("show_unplayable_in_list", True) and tag_type == self.TAG_UNPLAYABLE:
                        continue
                    
                    display_name = self.custom_names.get(rom_name, 
                                   self.game_aliases.get(rom_name, rom_basename))
                    
                    zip_filename = rom_basename
                    game_list.append({
                        "game_name": display_name,
                        "zip_filename": zip_filename
                    })
                
                game_list.sort(key=lambda x: x["game_name"].lower())
                
                # 写入CSV文件
                with open(file_path, 'w', encoding='utf-8', newline='') as f:
                    writer = csv.writer(f)
                    # 写入标题行
                    writer.writerow(["游戏名", "rom文件名"])
                    # 写入数据行
                    for game in game_list:
                        writer.writerow([game["game_name"], game["zip_filename"]])
                
                complete_dialog = Gtk.MessageDialog(
                    parent=dialog,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.INFO,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"成功导出 {len(game_list)} 个游戏信息到:\n{file_path}"
                )
                complete_dialog.run()
                complete_dialog.destroy()
                
            except Exception as e:
                error_dialog = Gtk.MessageDialog(
                    parent=dialog,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.ERROR,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"导出游戏列表失败: {str(e)}"
                )
                error_dialog.run()
                error_dialog.destroy()
        
        dialog.destroy()
    
    # 以下为其他保持不变的方法
    def _load_window_state(self):
        """加载窗口状态，固定为800x600"""
        return {
            "width": 800,
            "height": 600
        }
    
    def _save_window_state(self):
        """保存窗口状态（保持固定大小，不实际保存）"""
        return True
    
    def _ensure_save_states_dir(self):
        """确保存档目录存在"""
        if not os.path.exists(self.save_states_path):
            os.makedirs(self.save_states_path, exist_ok=True)
    
    def _ensure_advmame_config_dir(self):
        """确保配置目录存在"""
        config_dir = os.path.dirname(self.advmame_config_path)
        if not os.path.exists(config_dir):
            os.makedirs(config_dir, exist_ok=True)
            if not os.path.exists(self.advmame_config_path):
                with open(self.advmame_config_path, 'w') as f:
                    f.write("")
    
    def _load_advmame_config(self):
        """加载AdvanceMAME配置"""
        config = {}
        if os.path.exists(self.advmame_config_path):
            try:
                with open(self.advmame_config_path, 'r') as f:
                    for line in f:
                        line = line.strip()
                        if line and not line.startswith('#'):
                            separator_pos = line.find(' ')
                            if separator_pos != -1:
                                key = line[:separator_pos]
                                value = line[separator_pos+1:].strip()
                                config[key] = value
            except Exception as e:
                print(f"加载AdvanceMAME配置失败: {e}")
        return config
    
    def _save_advmame_config(self, config):
        """保存AdvanceMAME配置"""
        try:
            original_lines = []
            if os.path.exists(self.advmame_config_path):
                with open(self.advmame_config_path, 'r') as f:
                    original_lines = f.readlines()
            
            new_lines = []
            processed_keys = set()
            
            for line in original_lines:
                stripped_line = line.strip()
                if stripped_line and not stripped_line.startswith('#'):
                    separator_pos = stripped_line.find(' ')
                    if separator_pos != -1:
                        key = stripped_line[:separator_pos]
                        if key in config:
                            new_lines.append(f"{key} {config[key]}\n")
                            processed_keys.add(key)
                            continue
                new_lines.append(line)
            
            for key, value in config.items():
                if key not in processed_keys:
                    new_lines.append(f"{key} {value}\n")
            
            with open(self.advmame_config_path, 'w') as f:
                f.writelines(new_lines)
            return True
        except Exception as e:
            print(f"保存AdvanceMAME配置失败: {e}")
            return False
    
    def _load_theme_preference(self):
        """加载主题偏好"""
        if os.path.exists(self.theme_pref_file):
            try:
                with open(self.theme_pref_file, 'r', encoding='utf-8') as f:
                    return json.load(f).get('preference', self.THEME_AUTO)
            except (json.JSONDecodeError, IOError) as e:
                print(f"加载主题偏好失败: {e}")
        
        return self.THEME_AUTO
    
    def _save_theme_preference(self):
        """保存主题偏好"""
        try:
            with open(self.theme_pref_file, 'w', encoding='utf-8') as f:
                json.dump({'preference': self.theme_preference}, f, ensure_ascii=False, indent=2)
            return True
        except IOError as e:
            print(f"保存主题偏好失败: {e}")
            return False
    
    def _detect_dark_theme(self):
        """检测系统主题是否为深色"""
        settings = Gtk.Settings.get_default()
        if hasattr(settings, 'get_property'):
            try:
                if settings.get_property("gtk-application-prefer-dark-theme"):
                    return True
            except:
                pass
        
        widget = Gtk.Label()
        style_context = widget.get_style_context()
        bg_color = style_context.get_color(Gtk.StateFlags.NORMAL)
        brightness = (0.299 * bg_color.red + 
                     0.587 * bg_color.green + 
                     0.114 * bg_color.blue)
        return brightness < 0.5
    
    def _determine_theme(self):
        """确定当前主题"""
        if self.theme_preference == self.THEME_AUTO:
            self.theme_is_dark = self._detect_dark_theme()
        elif self.theme_preference == self.THEME_DARK:
            self.theme_is_dark = True
        else:
            self.theme_is_dark = False
    
    def _set_tag_colors_based_on_theme(self):
        """根据主题设置标签颜色"""
        if self.theme_is_dark:
            self.tag_colors = {
                self.TAG_NONE: None,
                self.TAG_GAME: "#00008B",
                self.TAG_BIOS: "#FFA500",
                self.TAG_UNPLAYABLE: "#8B0000"
            }
        else:
            self.tag_colors = {
                self.TAG_NONE: None,
                self.TAG_GAME: "#87CEFA",
                self.TAG_BIOS: "#FFFFE0",
                self.TAG_UNPLAYABLE: "#FFC0CB"
            }
    
    def _on_theme_changed(self, widget=None, preference=None):
        """处理主题变更"""
        if preference is not None:
            self.theme_preference = preference
            self._save_theme_preference()
            self._determine_theme()
            self._set_tag_colors_based_on_theme()
            if hasattr(self, 'game_liststore') and self.game_liststore is not None:
                self.refresh_rom_list()
    
    def _on_rgb_effect_toggled(self, widget, effect):
        """处理图形渲染效果变更"""
        if widget.get_active():
            config = self._load_advmame_config()
            config['display_rgbeffect'] = effect
            if self._save_advmame_config(config) and not self.initializing:
                dialog = Gtk.MessageDialog(
                    parent=self.window,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.INFO,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"图形渲染设置已更新为: {effect}"
                )
                dialog.run()
                dialog.destroy()
    
    def create_main_window(self):
        """创建主窗口，设置固定大小并移除最大化按钮"""
        self.window = Gtk.Window(title="AdvanceMAME Frontend")
        window_state = self._load_window_state()
        
        # 设置固定大小
        self.window.set_default_size(window_state["width"], window_state["height"])
        self.window.set_resizable(False)  # 禁止调整大小
        
        # 移除最大化按钮
        self.window.set_decorated(True)
        if hasattr(self.window, 'set_window') and self.window.get_window():
            hints = self.window.get_window().get_size_hints()
            hints.max_width = window_state["width"]
            hints.max_height = window_state["height"]
            hints.min_width = window_state["width"]
            hints.min_height = window_state["height"]
            self.window.get_window().set_size_hints(hints)
        
        # 在某些窗口管理器中，使用set_type_hint可能更有效
        self.window.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        
        self.window.connect("destroy", self.on_window_destroy)
        
        self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        self.window.add(self.vbox)
        self.create_menu_bar()
    
    def on_window_destroy(self, widget):
        """窗口关闭时保存状态"""
        self._save_window_state()
        Gtk.main_quit()
    
    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = Gtk.MenuBar()
        
        # 文件菜单
        file_menu = Gtk.Menu()
        file_item = Gtk.MenuItem(label="文件")
        file_item.set_submenu(file_menu)
        
        save_states_item = Gtk.MenuItem(label="游戏存档")
        save_states_item.connect("activate", self.open_save_states_window)
        file_menu.append(save_states_item)
        
        separator = Gtk.SeparatorMenuItem()
        file_menu.append(separator)
        
        exit_item = Gtk.MenuItem(label="退出")
        exit_item.connect("activate", self.on_window_destroy)
        file_menu.append(exit_item)
        
        # 设置菜单
        settings_menu = Gtk.Menu()
        settings_item = Gtk.MenuItem(label="设置")
        settings_item.set_submenu(settings_menu)
        
        app_settings_item = Gtk.MenuItem(label="设置")
        app_settings_item.connect("activate", self.open_settings_window)
        settings_menu.append(app_settings_item)
        
        # 主题子菜单
        theme_submenu = Gtk.Menu()
        theme_item = Gtk.MenuItem(label="主题")
        theme_item.set_submenu(theme_submenu)
        
        auto_item = Gtk.RadioMenuItem.new_with_label(None, "自动检测")
        auto_item.connect("activate", lambda w: self._on_theme_changed(preference=self.THEME_AUTO))
        theme_submenu.append(auto_item)
        
        dark_item = Gtk.RadioMenuItem.new_with_label_from_widget(auto_item, "深色主题")
        dark_item.connect("activate", lambda w: self._on_theme_changed(preference=self.THEME_DARK))
        theme_submenu.append(dark_item)
        
        light_item = Gtk.RadioMenuItem.new_with_label_from_widget(auto_item, "浅色主题")
        light_item.connect("activate", lambda w: self._on_theme_changed(preference=self.THEME_LIGHT))
        theme_submenu.append(light_item)
        
        if self.theme_preference == self.THEME_DARK:
            dark_item.set_active(True)
        elif self.theme_preference == self.THEME_LIGHT:
            light_item.set_active(True)
        else:
            auto_item.set_active(True)
        
        # 按键映射
        keymap_item = Gtk.MenuItem(label="按键映射")
        keymap_item.connect("activate", self.open_keymap_window)
        settings_menu.append(keymap_item)
        settings_menu.append(theme_item)
        
        # 帮助菜单
        help_menu = Gtk.Menu()
        help_item = Gtk.MenuItem(label="帮助")
        help_item.set_submenu(help_menu)
        
        shortcuts_item = Gtk.MenuItem(label="游戏快捷键")
        shortcuts_item.connect("activate", self.show_shortcuts_dialog)
        help_menu.append(shortcuts_item)
        
        about_item = Gtk.MenuItem(label="关于")
        about_item.connect("activate", self.show_about_dialog)
        help_menu.append(about_item)
        
        menubar.append(file_item)
        menubar.append(settings_item)
        menubar.append(help_item)
        self.vbox.pack_start(menubar, False, False, 0)
    
    def calculate_stats(self):
        """计算统计数据"""
        rom_files = glob.glob(os.path.join(self.rom_path, "*.zip"))
        rom_total = len(rom_files)
        
        bios_count = 0
        game_count = 0
        unplayable_count = 0
        
        for rom_file in rom_files:
            rom_basename = os.path.basename(rom_file)
            rom_name = os.path.splitext(rom_basename)[0]
            
            tag_type = self.game_tags.get(rom_name, self.TAG_NONE)
            
            if tag_type == self.TAG_BIOS:
                bios_count += 1
            elif tag_type == self.TAG_UNPLAYABLE:
                unplayable_count += 1
            else:
                game_count += 1
        
        return rom_total, bios_count, game_count, unplayable_count
    
    def refresh_stats(self, widget, stats_grid):
        """刷新统计数据"""
        rom_total, bios_count, game_count, unplayable_count = self.calculate_stats()
        
        rom_total_label = stats_grid.get_child_at(1, 0)
        if rom_total_label:
            rom_total_label.set_text(str(rom_total))
        
        bios_count_label = stats_grid.get_child_at(1, 1)
        if bios_count_label:
            bios_count_label.set_text(str(bios_count))
        
        game_count_label = stats_grid.get_child_at(1, 2)
        if game_count_label:
            game_count_label.set_text(str(game_count))
        
        unplayable_count_label = stats_grid.get_child_at(1, 3)
        if unplayable_count_label:
            unplayable_count_label.set_text(str(unplayable_count))
    
    def on_bios_checkbox_toggled(self, widget):
        """处理BIOS显示设置"""
        self.settings["show_bios_in_list"] = widget.get_active()
        self._save_settings()
        self.refresh_rom_list()
    
    def on_unplayable_checkbox_toggled(self, widget):
        """处理不可玩游戏显示设置"""
        self.settings["show_unplayable_in_list"] = widget.get_active()
        self._save_settings()
        self.refresh_rom_list()
    
    def show_shortcuts_dialog(self, widget):
        """显示快捷键对话框"""
        shortcuts_window = Gtk.Window(title="游戏快捷键")
        shortcuts_window.set_default_size(400, 300)
        shortcuts_window.set_transient_for(self.window)
        shortcuts_window.set_modal(True)
        
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox.set_border_width(20)
        shortcuts_window.add(vbox)
        
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        
        container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        container.set_border_width(10)
        scrolled_window.add(container)
        
        grid = Gtk.Grid()
        grid.set_column_spacing(10)
        grid.set_row_spacing(5)
        container.pack_start(grid, False, False, 0)
        
        shortcuts = [
            ("5, 6", "投币"),
            ("1, 2", "开始游戏"),
            ("Shift+F7", "存档"),
            ("F7", "读取存档"),
            ("Tab", "显示MAME菜单"),
            ("p", "暂停游戏"),
            ("Esc", "调出游戏菜单")
        ]
        
        for i, (shortcut, description) in enumerate(shortcuts):
            shortcut_label = Gtk.Label(label=shortcut)
            shortcut_label.set_xalign(0)
            shortcut_label.set_width_chars(10)
            grid.attach(shortcut_label, 0, i, 1, 1)
            
            desc_label = Gtk.Label(label=f"- {description}")
            desc_label.set_xalign(0)
            grid.attach(desc_label, 1, i, 1, 1)
        
        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        button_box.set_homogeneous(False)
        vbox.pack_start(button_box, False, False, 10)
        
        close_button = Gtk.Button(label="关闭")
        close_button.set_hexpand(False)
        close_button.set_halign(Gtk.Align.END)
        close_button.connect("clicked", lambda w: shortcuts_window.destroy())
        button_box.pack_end(close_button, False, False, 0)
        
        shortcuts_window.show_all()
    
    def open_save_states_window(self, widget):
        """打开存档管理窗口"""
        save_window = Gtk.Window(title="游戏存档管理")
        save_window.set_default_size(667, 500)
        save_window.set_transient_for(self.window)
        save_window.set_modal(True)
        
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox.set_border_width(10)
        save_window.add(vbox)
        
        toolbar = Gtk.Toolbar()
        vbox.pack_start(toolbar, False, False, 0)
        
        refresh_button = Gtk.ToolButton.new(Gtk.Image.new_from_icon_name(
            "view-refresh", Gtk.IconSize.BUTTON), "刷新")
        refresh_button.set_tooltip_text("刷新存档列表")
        
        save_liststore = Gtk.ListStore(str, str, int, str, str)
        refresh_button.connect("clicked", lambda w: self.refresh_save_states_list(save_liststore))
        toolbar.insert(refresh_button, 0)
        
        separator = Gtk.SeparatorToolItem()
        toolbar.insert(separator, 1)
        
        backup_button = Gtk.ToolButton.new(Gtk.Image.new_from_icon_name(
            "document-save-as", Gtk.IconSize.BUTTON), "备份")
        backup_button.set_tooltip_text("备份所有存档")
        backup_button.connect("clicked", self.backup_all_save_states)
        toolbar.insert(backup_button, 2)
        
        restore_button = Gtk.ToolButton.new(Gtk.Image.new_from_icon_name(
            "document-revert", Gtk.IconSize.BUTTON), "还原")
        restore_button.set_tooltip_text("从备份还原存档")
        restore_button.connect("clicked", self.restore_save_states)
        toolbar.insert(restore_button, 3)
        
        separator = Gtk.SeparatorToolItem()
        toolbar.insert(separator, 4)
        
        delete_button = Gtk.ToolButton.new(Gtk.Image.new_from_icon_name(
            "edit-delete", Gtk.IconSize.BUTTON), "删除")
        delete_button.set_tooltip_text("删除选中的存档")
        
        treeview = Gtk.TreeView(model=save_liststore)
        treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
        treeview.set_name("save_states_treeview")
        
        delete_button.connect("clicked", self.delete_selected_save_state, save_liststore, treeview)
        toolbar.insert(delete_button, 5)
        
        self.total_size_label = Gtk.Label()
        self.total_size_label.set_property("xalign", 0)
        vbox.pack_start(self.total_size_label, False, False, 5)
        
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        
        scrolled_window.add(treeview)
        
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("存档文件名", renderer, text=0)
        column.set_sort_column_id(0)
        treeview.append_column(column)
        
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("归属游戏名", renderer, text=1)
        column.set_sort_column_id(1)
        treeview.append_column(column)
        
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("存档大小", renderer, text=3)
        column.set_sort_column_id(2)
        treeview.append_column(column)
        
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("最后修改时间", renderer, text=4)
        column.set_sort_column_id(4)
        treeview.append_column(column)
        
        close_button = Gtk.Button(label="关闭")
        close_button.set_hexpand(False)
        close_button.set_halign(Gtk.Align.CENTER)
        close_button.connect("clicked", lambda w: save_window.destroy())
        vbox.pack_start(close_button, False, False, 0)
        
        self.refresh_save_states_list(save_liststore)
        save_window.show_all()
    
    def refresh_save_states_list(self, liststore):
        """刷新存档列表"""
        liststore.clear()
        
        save_files = glob.glob(os.path.join(self.save_states_path, "*"))
        total_size_bytes = 0
        file_count = 0
        
        for file_path in save_files:
            if not os.path.isfile(file_path):
                continue
                
            file_name = os.path.basename(file_path)
            file_count += 1
                
            game_name = os.path.splitext(file_name)[0]
            display_name = self.custom_names.get(game_name, 
                           self.game_aliases.get(game_name, game_name))
            
            try:
                file_size = os.path.getsize(file_path)
                total_size_bytes += file_size
                if file_size < 1024:
                    size_str = f"{file_size} B"
                elif file_size < 1024 * 1024:
                    size_str = f"{file_size / 1024:.2f} KB"
                else:
                    size_str = f"{file_size / (1024 * 1024):.2f} MB"
            except Exception as e:
                file_size = 0
                size_str = "未知"
            
            try:
                mtime = os.path.getmtime(file_path)
                modified_time = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S")
            except Exception as e:
                modified_time = "未知"
            
            liststore.append([file_name, display_name, file_size, size_str, modified_time])
        
        if total_size_bytes < 1024:
            total_size_str = f"{total_size_bytes} B"
        elif total_size_bytes < 1024 * 1024:
            total_size_str = f"{total_size_bytes / 1024:.2f} KB"
        else:
            total_size_str = f"{total_size_bytes / (1024 * 1024):.2f} MB"
        
        self.total_size_label.set_text(f"共 {file_count} 个存档占用 {total_size_str} 空间")
    
    def backup_all_save_states(self, widget):
        """备份所有存档"""
        save_files = glob.glob(os.path.join(self.save_states_path, "*"))
        if not save_files:
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                text="没有找到任何存档文件，无需备份。"
            )
            dialog.run()
            dialog.destroy()
            return
        
        dialog = Gtk.FileChooserDialog(
            title="保存存档备份",
            parent=self.window,
            action=Gtk.FileChooserAction.SAVE
        )
        dialog.add_buttons(
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_SAVE, Gtk.ResponseType.OK
        )
        
        date_str = datetime.now().strftime("%Y%m%d_%H%M%S")
        default_filename = f"mame_{date_str}.tar.gz"
        dialog.set_current_name(default_filename)
        
        filter_tar = Gtk.FileFilter()
        filter_tar.set_name("Tar.gz 归档文件")
        filter_tar.add_pattern("*.tar.gz")
        dialog.add_filter(filter_tar)
        
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            backup_path = dialog.get_filename()
            if not backup_path.endswith(".tar.gz"):
                backup_path += ".tar.gz"
            
            try:
                with tarfile.open(backup_path, "w:gz") as tar:
                    for file_path in save_files:
                        if os.path.isfile(file_path):
                            file_name = os.path.basename(file_path)
                            tar.add(file_path, arcname=file_name)
                
                dialog = Gtk.MessageDialog(
                    parent=self.window,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.INFO,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"成功备份 {len(save_files)} 个存档文件到:\n{backup_path}"
                )
                dialog.run()
                dialog.destroy()
                
            except Exception as e:
                dialog = Gtk.MessageDialog(
                    parent=self.window,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.ERROR,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"备份存档失败: {str(e)}"
                )
                dialog.run()
                dialog.destroy()
        
        dialog.destroy()
    
    def restore_save_states(self, widget):
        """从备份还原存档"""
        dialog = Gtk.FileChooserDialog(
            title="选择存档备份文件",
            parent=self.window,
            action=Gtk.FileChooserAction.OPEN
        )
        dialog.add_buttons(
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_OPEN, Gtk.ResponseType.OK
        )
        
        filter_tar = Gtk.FileFilter()
        filter_tar.set_name("Tar.gz 归档文件")
        filter_tar.add_pattern("*.tar.gz")
        dialog.add_filter(filter_tar)
        
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            backup_path = dialog.get_filename()
            
            confirm_dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.WARNING,
                buttons=Gtk.ButtonsType.YES_NO,
                text="确定要从备份还原存档吗？\n这将覆盖现有的同名存档文件。"
            )
            confirm_response = confirm_dialog.run()
            confirm_dialog.destroy()
            
            if confirm_response == Gtk.ResponseType.YES:
                try:
                    with tarfile.open(backup_path, "r:gz") as tar:
                        members = tar.getmembers()
                        file_count = 0
                        
                        for member in members:
                            if member.isfile():
                                member.name = os.path.basename(member.name)
                                tar.extract(member, path=self.save_states_path)
                                file_count += 1
                    
                    dialog = Gtk.MessageDialog(
                        parent=self.window,
                        modal=True,
                        destroy_with_parent=True,
                        message_type=Gtk.MessageType.INFO,
                        buttons=Gtk.ButtonsType.OK,
                        text=f"成功从备份还原 {file_count} 个存档文件到:\n{self.save_states_path}"
                    )
                    dialog.run()
                    dialog.destroy()
                    
                except Exception as e:
                    dialog = Gtk.MessageDialog(
                        parent=self.window,
                        modal=True,
                        destroy_with_parent=True,
                        message_type=Gtk.MessageType.ERROR,
                        buttons=Gtk.ButtonsType.OK,
                        text=f"还原存档失败: {str(e)}"
                    )
                    dialog.run()
                    dialog.destroy()
        
        dialog.destroy()
    
    def delete_selected_save_state(self, widget, liststore, treeview):
        """删除选中的存档"""
        if not isinstance(treeview, Gtk.TreeView):
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text="无法获取存档列表控件。"
            )
            dialog.run()
            dialog.destroy()
            return
        
        selection = treeview.get_selection()
        model, paths = selection.get_selected_rows()
        
        if not paths:
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.WARNING,
                buttons=Gtk.ButtonsType.OK,
                text="请先选择要删除的存档文件。"
            )
            dialog.run()
            dialog.destroy()
            return
        
        filenames = [model[path][0] for path in paths]
        file_count = len(filenames)
        
        confirm_dialog = Gtk.MessageDialog(
            parent=self.window,
            modal=True,
            destroy_with_parent=True,
            message_type=Gtk.MessageType.WARNING,
            buttons=Gtk.ButtonsType.YES_NO,
            text=f"确定要删除选中的 {file_count} 个存档文件吗？\n此操作无法撤销。"
        )
        confirm_response = confirm_dialog.run()
        confirm_dialog.destroy()
        
        if confirm_response == Gtk.ResponseType.YES:
            deleted_count = 0
            error_files = []
            
            for filename in filenames:
                file_path = os.path.join(self.save_states_path, filename)
                try:
                    if os.path.exists(file_path):
                        os.remove(file_path)
                        deleted_count += 1
                except Exception as e:
                    error_files.append(f"{filename}: {str(e)}")
            
            self.refresh_save_states_list(liststore)
            
            if error_files:
                message = f"成功删除 {deleted_count} 个存档文件，但部分文件删除失败:\n"
                message += "\n".join(error_files)
                dialog_type = Gtk.MessageType.WARNING
            else:
                message = f"成功删除 {deleted_count} 个存档文件。"
                dialog_type = Gtk.MessageType.INFO
            
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=dialog_type,
                buttons=Gtk.ButtonsType.OK,
                text=message
            )
            dialog.run()
            dialog.destroy()
    
    def open_keymap_window(self, widget):
        """打开按键映射窗口"""
        keymap_window = Gtk.Window(title="按键映射设置")
        keymap_window.set_default_size(600, 500)
        keymap_window.set_transient_for(self.window)
        keymap_window.set_modal(True)
        
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox.set_border_width(10)
        keymap_window.add(vbox)
   
        info_label = Gtk.Label(label="以下是当前的按键映射设置，点击捕获按钮可以重新映射按键")
        info_label.set_line_wrap(True)
        vbox.pack_start(info_label, False, False, 0)
        
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        
        grid = Gtk.Grid()
        grid.set_row_spacing(10)
        grid.set_column_spacing(10)
        grid.set_border_width(10)
        scrolled_window.add(grid)
        
        config = self._load_advmame_config()
        
        keys_to_configure = {
            'input_map[p1_left]': '左方向',
            'input_map[p1_right]': '右方向',
            'input_map[p1_up]': '上方向',
            'input_map[p1_down]': '下方向',
            'input_map[p1_button1]': '按钮1',
            'input_map[p1_button2]': '按钮2',
            'input_map[p1_button3]': '按钮3',
            'input_map[p1_button4]': '按钮4',
            'input_map[p1_button5]': '按钮5',
            'input_map[p1_button6]': '按钮6'
        }
        
        key_entries = {}
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>功能</b>")
        grid.attach(label, 0, 0, 1, 1)
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>当前按键</b>")
        grid.attach(label, 1, 0, 1, 1)
        
        label = Gtk.Label()
        label.set_property("xalign", 0)
        label.set_markup("<b>新按键设置</b>")
        grid.attach(label, 2, 0, 1, 1)
        
        row = 1
        for config_key, display_name in keys_to_configure.items():
            label = Gtk.Label(label=display_name)
            label.set_property("xalign", 0)
            grid.attach(label, 0, row, 1, 1)
            
            current_value = config.get(config_key, "未设置")
            key_char = self._extract_key_from_config(current_value)
            display_value = key_char if key_char else current_value
            
            current_label = Gtk.Label(label=display_value)
            current_label.set_property("xalign", 0)
            grid.attach(current_label, 1, row, 1, 1)
            
            hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
            
            entry = Gtk.Entry()
            entry.set_editable(False)
            entry.set_text(display_value)
            entry.set_property("xalign", 0)
            hbox.pack_start(entry, True, True, 0)
            
            capture_button = Gtk.Button(label="捕获")
            capture_button.connect("clicked", self.capture_key_press, entry, keymap_window, config_key)
            hbox.pack_start(capture_button, False, False, 0)
            
            grid.attach(hbox, 2, row, 1, 1)
            
            key_entries[config_key] = entry
            
            row += 1
        
        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        vbox.pack_start(button_box, False, False, 0)
        
        reset_button = Gtk.Button(label="重置为默认")
        reset_button.connect("clicked", self.reset_keymap_defaults, key_entries, config, keys_to_configure)
        button_box.pack_end(reset_button, False, False, 0)
        
        cancel_button = Gtk.Button(label="取消")
        cancel_button.connect("clicked", lambda w: keymap_window.destroy())
        button_box.pack_end(cancel_button, False, False, 0)
        
        apply_button = Gtk.Button(label="应用")
        apply_button.connect("clicked", self.save_keymap_settings, key_entries, keymap_window)
        button_box.pack_end(apply_button, False, False, 0)
        
        keymap_window.show_all()
    
    def _extract_key_from_config(self, value):
        """从配置中提取按键"""
        if value.startswith("keyboard[") and "]" in value:
            inner_part = value[len("keyboard["):-1]
            parts = inner_part.split(',')
            if len(parts) >= 2:
                return parts[1].strip()
        return None
    
    def _format_key_for_config(self, key_char, original_value):
        """格式化按键配置"""
        if original_value.startswith("keyboard[") and "]" in original_value:
            inner_part = original_value[len("keyboard["):-1]
            parts = inner_part.split(',')
            if len(parts) >= 1:
                device_id = parts[0].strip()
                return f"keyboard[{device_id},{key_char}]"
        
        return f"keyboard[0,{key_char}]"
    
    def reset_keymap_defaults(self, widget, key_entries, original_config, keys_to_configure):
        """重置按键映射为默认"""
        for config_key, entry in key_entries.items():
            original_value = original_config.get(config_key, "未设置")
            key_char = self._extract_key_from_config(original_value)
            display_value = key_char if key_char else original_value
            entry.set_text(display_value)
    
    def capture_key_press(self, widget, entry, window, config_key):
        """捕获按键"""
        widget.set_label("请按任意键...")
        widget.set_sensitive(False)
        
        capture_dialog = Gtk.Dialog(
            title="捕获按键",
            parent=window,
            modal=True
        )
        capture_dialog.set_default_size(300, 100)
        
        label = Gtk.Label(label="请按下要映射的按键，或按ESC取消")
        capture_dialog.get_content_area().pack_start(label, True, True, 10)
        
        capture_dialog.show_all()
        
        def on_key_press(dialog, event):
            widget.set_sensitive(True)
            if event.keyval == Gdk.KEY_Escape:
                dialog.destroy()
                widget.set_label("捕获")
                return True
                
            key_name = Gdk.keyval_name(event.keyval)
            if key_name:
                entry.set_text(key_name)
                dialog.destroy()
                widget.set_label("捕获")
                return True
            return False
        
        def on_dialog_close(dialog, response):
            widget.set_sensitive(True)
            widget.set_label("捕获")
            dialog.destroy()
        
        capture_dialog.connect("key-press-event", on_key_press)
        capture_dialog.connect("response", on_dialog_close)
        capture_dialog.run()
    
    def save_keymap_settings(self, widget, key_entries, window):
        """保存按键映射"""
        config = self._load_advmame_config()
        
        changed = False
        for config_key, entry in key_entries.items():
            new_key_char = entry.get_text().strip()
            original_value = config.get(config_key, "")
            
            if new_key_char:
                new_value = self._format_key_for_config(new_key_char, original_value)
                
                if new_value != original_value:
                    config[config_key] = new_value
                    changed = True
        
        if not changed:
            window.destroy()
            return
        
        if self._save_advmame_config(config):
            dialog = Gtk.MessageDialog(
                parent=window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                text="按键映射设置已保存"
            )
            dialog.run()
            dialog.destroy()
            window.destroy()
        else:
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text="保存按键映射设置失败"
            )
            dialog.run()
            dialog.destroy()
    
    def show_about_dialog(self, widget):
        """显示关于对话框"""
        about_dialog = Gtk.AboutDialog(parent=self.window)
        about_dialog.set_program_name("AdvanceMAME Frontend")
        about_dialog.set_version(self.version)
        about_dialog.set_copyright("© 2023 豆包")
        about_dialog.set_comments("豆包开发的MAME模拟器前端，用于管理和启动MAME游戏。")
        about_dialog.set_website("https://www.doubao.com")
        about_dialog.set_website_label("豆包官网")
        about_dialog.set_authors(["豆包开发团队"])
        about_dialog.set_logo_icon_name("applications-games")
        about_dialog.run()
        about_dialog.destroy()
    
    def create_ui_elements(self):
        """创建UI元素"""
        self.header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        self.header.set_border_width(10)
        self.vbox.pack_start(self.header, False, False, 0)
        
        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        
        refresh_button = Gtk.Button.new_from_icon_name("view-refresh", Gtk.IconSize.BUTTON)
        refresh_button.set_tooltip_text("刷新游戏列表")
        refresh_button.connect("clicked", self.refresh_rom_list)
        button_box.pack_start(refresh_button, False, False, 0)
        
        cache_button = Gtk.Button.new_from_icon_name("system-run", Gtk.IconSize.BUTTON)
        cache_button.set_tooltip_text("刷新游戏别名缓存")
        cache_button.connect("clicked", self.refresh_game_aliases)
        button_box.pack_start(cache_button, False, False, 0)
        
        self.header.pack_start(button_box, False, False, 0)
        
        search_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        search_box.set_hexpand(True)
        
        search_icon = Gtk.Image.new_from_icon_name("system-search", Gtk.IconSize.BUTTON)
        search_box.pack_start(search_icon, False, False, 0)
        
        self.search_entry = Gtk.Entry()
        self.search_entry.set_placeholder_text("搜索游戏...")
        self.search_entry.connect("changed", self.on_search_changed)
        search_box.pack_start(self.search_entry, True, True, 0)
        
        clear_button = Gtk.Button.new_from_icon_name("edit-clear", Gtk.IconSize.BUTTON)
        clear_button.set_tooltip_text("清除搜索")
        clear_button.connect("clicked", lambda w: self.search_entry.set_text(""))
        search_box.pack_start(clear_button, False, False, 0)
        
        self.header.pack_start(search_box, True, True, 5)
        
        self.notebook = Gtk.Notebook()
        self.vbox.pack_start(self.notebook, True, True, 0)
        
        self.create_game_list()
        game_label = Gtk.Label(label="游戏列表")
        self.notebook.append_page(self.game_scrolled_window, game_label)
        
        self.create_recent_list()
        recent_label = Gtk.Label(label="最近游玩")
        self.notebook.append_page(self.recent_scrolled_window, recent_label)
        
        self.statusbar = Gtk.Statusbar()
        self.vbox.pack_end(self.statusbar, False, False, 0)
        self.status_context = self.statusbar.get_context_id("Game count")
    
    def on_search_changed(self, widget):
        """处理搜索"""
        search_text = widget.get_text().lower()
        
        if not search_text:
            self.refresh_rom_list()
            return
        
        self.game_liststore.clear()
        
        rom_files = glob.glob(os.path.join(self.rom_path, "*.zip"))
        game_entries = []
        
        for rom_file in rom_files:
            rom_basename = os.path.basename(rom_file)
            rom_name = os.path.splitext(rom_basename)[0]
            
            display_rom_name = self._truncate_zip_name(rom_name)
            tag_type = self.game_tags.get(rom_name, self.TAG_NONE)
            
            if not self.settings.get("show_bios_in_list", True) and tag_type == self.TAG_BIOS:
                continue
            
            if not self.settings.get("show_unplayable_in_list", True) and tag_type == self.TAG_UNPLAYABLE:
                continue
            
            display_name = self.custom_names.get(rom_name, 
                           self.game_aliases.get(rom_name, rom_basename))
            
            if (search_text in display_name.lower() or 
                search_text in rom_name.lower()):
                game_entries.append((rom_name, display_name, tag_type, display_rom_name))
        
        game_entries.sort(key=lambda x: x[1].lower())
        
        for entry in game_entries:
            self.game_liststore.append((entry[0], entry[1], entry[2], entry[3]))
        
        count = len(game_entries)
        self.statusbar.push(self.status_context, f"找到 {count} 个匹配的游戏")
    
    def _truncate_zip_name(self, rom_name):
        """截断ZIP文件名"""
        if len(rom_name) > 8:
            return rom_name[:8] + "..."
        return rom_name
    
    def _ensure_cache_dir(self):
        """确保缓存目录存在"""
        if not os.path.exists(self.cache_dir):
            os.makedirs(self.cache_dir, exist_ok=True)
    
    def _load_game_aliases(self):
        """加载游戏别名"""
        if os.path.exists(self.cache_file):
            try:
                with open(self.cache_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError) as e:
                print(f"加载缓存失败: {e}")
                if os.path.exists(self.cache_file):
                    os.remove(self.cache_file)
        
        return self.refresh_game_aliases()
    
    def _load_recent_games(self):
        """加载最近游戏"""
        if os.path.exists(self.recent_file):
            try:
                with open(self.recent_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError) as e:
                print(f"加载最近游玩记录失败: {e}")
                if os.path.exists(self.recent_file):
                    os.remove(self.recent_file)
        
        return []
    
    def _load_game_tags(self):
        """加载游戏标签"""
        if os.path.exists(self.tags_file):
            try:
                with open(self.tags_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError) as e:
                print(f"加载游戏标记失败: {e}")
                if os.path.exists(self.tags_file):
                    os.remove(self.tags_file)
        
        return {}
    
    def _load_custom_names(self):
        """加载自定义名称"""
        if os.path.exists(self.custom_names_file):
            try:
                with open(self.custom_names_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError) as e:
                print(f"加载自定义名称失败: {e}")
                if os.path.exists(self.custom_names_file):
                    os.remove(self.custom_names_file)
        
        return {}
    
    def _save_custom_names(self):
        """保存自定义名称"""
        try:
            with open(self.custom_names_file, 'w', encoding='utf-8') as f:
                json.dump(self.custom_names, f, ensure_ascii=False, indent=2)
            return True
        except IOError as e:
            print(f"保存自定义名称失败: {e}")
            return False
    
    def _save_game_tags(self):
        """保存游戏标签"""
        try:
            with open(self.tags_file, 'w', encoding='utf-8') as f:
                json.dump(self.game_tags, f, ensure_ascii=False, indent=2)
            return True
        except IOError as e:
            print(f"保存游戏标记失败: {e}")
            return False
    
    def _save_recent_game(self, rom_name):
        """保存最近游戏"""
        game_name = self.custom_names.get(rom_name, self.game_aliases.get(rom_name, rom_name))
        
        new_entry = {
            "rom_name": rom_name,
            "game_name": game_name,
            "timestamp": time.time(),
            "played_at": datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M")
        }
        
        updated_recent = [entry for entry in self.recent_games if entry["rom_name"] != rom_name]
        updated_recent.insert(0, new_entry)
        self.recent_games = updated_recent[:self.max_recent]
        
        try:
            with open(self.recent_file, 'w', encoding='utf-8') as f:
                json.dump(self.recent_games, f, ensure_ascii=False, indent=2)
            return True
        except IOError as e:
            print(f"保存最近游玩记录失败: {e}")
            return False
    
    def refresh_game_aliases(self, widget=None):
        """刷新游戏别名"""
        try:
            result = subprocess.run(
                [self.advmame_path, "--listxml"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            
            if result.returncode != 0:
                dialog = Gtk.MessageDialog(
                    parent=self.window,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.ERROR,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"获取游戏信息失败: {result.stderr}"
                )
                dialog.run()
                dialog.destroy()
                return {}
            
            aliases = {}
            try:
                root = ET.fromstring(result.stdout)
                for game in root.findall('game'):
                    rom_name = game.get('name')
                    if rom_name:
                        description = game.findtext('description', default=rom_name)
                        aliases[rom_name] = description.strip()
            
            except ET.ParseError as e:
                dialog = Gtk.MessageDialog(
                    parent=self.window,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.ERROR,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"解析游戏信息失败: {str(e)}"
                )
                dialog.run()
                dialog.destroy()
                return {}
            
            with open(self.cache_file, 'w', encoding='utf-8') as f:
                json.dump(aliases, f, ensure_ascii=False, indent=2)
            
            if widget:
                dialog = Gtk.MessageDialog(
                    parent=self.window,
                    modal=True,
                    destroy_with_parent=True,
                    message_type=Gtk.MessageType.INFO,
                    buttons=Gtk.ButtonsType.OK,
                    text=f"游戏别名缓存已更新，共 {len(aliases)} 个游戏"
                )
                dialog.run()
                dialog.destroy()
            
            self.game_aliases = aliases
            return aliases
            
        except Exception as e:
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text=f"更新游戏别名时出错: {str(e)}"
            )
            dialog.run()
            dialog.destroy()
            return {}
    
    def create_game_list(self):
        """创建游戏列表"""
        self.game_scrolled_window = Gtk.ScrolledWindow()
        self.game_scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        
        self.game_liststore = Gtk.ListStore(str, str, int, str)
        
        self.game_treeview = Gtk.TreeView(model=self.game_liststore)
        self.game_treeview.connect("row-activated", self.on_game_row_activated)
        
        self.create_context_menu()
        
        renderer_name = Gtk.CellRendererText()
        column_name = Gtk.TreeViewColumn("游戏名称", renderer_name, text=1)
        column_name.set_sort_column_id(1)
        column_name.set_resizable(True)
        column_name.set_min_width(200)
        column_name.set_expand(True)
        
        def set_background(column, cell, model, iter_, data):
            tag_type = model.get_value(iter_, 2)
            color = self.tag_colors.get(tag_type, None)
            cell.set_property('background', color)
        
        column_name.set_cell_data_func(renderer_name, set_background)
        self.game_treeview.append_column(column_name)
        
        renderer_filename = Gtk.CellRendererText()
        column_filename = Gtk.TreeViewColumn("ZIP文件名", renderer_filename, text=3)
        column_filename.set_sort_column_id(0)
        column_filename.set_resizable(True)
        column_filename.set_min_width(100)
        column_filename.set_max_width(120)
        
        column_filename.set_cell_data_func(renderer_filename, set_background)
        self.game_treeview.append_column(column_filename)
        
        self.game_scrolled_window.add(self.game_treeview)
    
    def create_context_menu(self):
        """创建右键菜单"""
        self.menu = Gtk.Menu()
        
        rename_item = Gtk.MenuItem(label="重命名")
        rename_item.connect("activate", self.on_rename_game)
        self.menu.append(rename_item)
        
        properties_item = Gtk.MenuItem(label="属性")
        properties_item.connect("activate", self.on_show_properties)
        self.menu.append(properties_item)
        
        separator1 = Gtk.SeparatorMenuItem()
        self.menu.append(separator1)
        
        game_item = Gtk.MenuItem(label="标记为游戏")
        game_item.connect("activate", self.on_mark_as_game)
        self.menu.append(game_item)
        
        bios_item = Gtk.MenuItem(label="标记为BIOS")
        bios_item.connect("activate", self.on_mark_as_bios)
        self.menu.append(bios_item)
        
        unplayable_item = Gtk.MenuItem(label="标记为不可玩")
        unplayable_item.connect("activate", self.on_mark_as_unplayable)
        self.menu.append(unplayable_item)
        
        separator2 = Gtk.SeparatorMenuItem()
        self.menu.append(separator2)
        
        clear_item = Gtk.MenuItem(label="清除标记")
        clear_item.connect("activate", self.on_clear_tag)
        self.menu.append(clear_item)
        
        self.menu.show_all()
        self.game_treeview.connect("button-press-event", self.on_treeview_button_press)
        self.selected_path = None
    
    def on_rename_game(self, widget):
        """重命名游戏"""
        if self.selected_path:
            iter_ = self.game_liststore.get_iter(self.selected_path)
            rom_name = self.game_liststore.get_value(iter_, 0)
            current_name = self.game_liststore.get_value(iter_, 1)
            
            original_name = self.game_aliases.get(rom_name, f"{rom_name}.zip")
            
            dialog = Gtk.Dialog(
                title="重命名游戏",
                parent=self.window,
                modal=True,
                destroy_with_parent=True
            )
            dialog.add_buttons(
                Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OK, Gtk.ResponseType.OK
            )
            
            box = dialog.get_content_area()
            box.set_border_width(10)
            box.set_spacing(5)
            
            label = Gtk.Label(label="请输入新名称:")
            box.pack_start(label, True, True, 0)
            
            entry = Gtk.Entry()
            entry.set_text(current_name)
            box.pack_start(entry, True, True, 0)
            
            hint_label = Gtk.Label()
            hint_label.set_markup(f"<small>提示: 留空将恢复为默认名称: {original_name}</small>")
            hint_label.set_line_wrap(True)
            box.pack_start(hint_label, True, True, 0)
            
            dialog.show_all()
            
            response = dialog.run()
            if response == Gtk.ResponseType.OK:
                new_name = entry.get_text().strip()
                
                if not new_name:
                    new_name = original_name
                
                if new_name != current_name:
                    self.game_liststore.set_value(iter_, 1, new_name)
                    if new_name == original_name and rom_name in self.custom_names:
                        del self.custom_names[rom_name]
                    else:
                        self.custom_names[rom_name] = new_name
                    
                    self._save_custom_names()
                    self.refresh_recent_list()
            
            dialog.destroy()
    
    def on_show_properties(self, widget):
        """显示游戏属性"""
        if self.selected_path:
            iter_ = self.game_liststore.get_iter(self.selected_path)
            rom_name = self.game_liststore.get_value(iter_, 0)
            display_name = self.game_liststore.get_value(iter_, 1)
            
            rom_file = os.path.join(self.rom_path, f"{rom_name}.zip")
            
            try:
                file_size_bytes = os.path.getsize(rom_file)
                file_size_mb = file_size_bytes / (1024 * 1024)
                file_size_str = f"{file_size_mb:.2f} MB"
            except Exception as e:
                file_size_str = f"无法获取 ({str(e)})"
            
            detected_name = self.game_aliases.get(rom_name, "未知")
            
            prop_window = Gtk.Window(title="游戏属性")
            prop_window.set_default_size(350, 200)
            prop_window.set_transient_for(self.window)
            prop_window.set_modal(True)
            
            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
            vbox.set_border_width(15)
            prop_window.add(vbox)
            
            title_label = Gtk.Label()
            title_label.set_markup("<span size='large' weight='bold'>游戏信息</span>")
            vbox.pack_start(title_label, False, False, 0)
            
            grid = Gtk.Grid()
            grid.set_row_spacing(10)
            grid.set_column_spacing(15)
            vbox.pack_start(grid, True, True, 0)
            
            row = 0
            
            label = Gtk.Label()
            label.set_property("xalign", 0)
            label.set_markup("<b>文件名:</b>")
            grid.attach(label, 0, row, 1, 1)
            
            value_label = Gtk.Label(label=f"{rom_name}.zip")
            value_label.set_property("xalign", 0)
            grid.attach(value_label, 1, row, 1, 1)
            row += 1
            
            label = Gtk.Label()
            label.set_property("xalign", 0)
            label.set_markup("<b>识别名称:</b>")
            grid.attach(label, 0, row, 1, 1)
            
            value_label = Gtk.Label(label=detected_name)
            value_label.set_property("xalign", 0)
            grid.attach(value_label, 1, row, 1, 1)
            row += 1
            
            label = Gtk.Label()
            label.set_property("xalign", 0)
            label.set_markup("<b>当前名称:</b>")
            grid.attach(label, 0, row, 1, 1)
            
            value_label = Gtk.Label(label=display_name)
            value_label.set_property("xalign", 0)
            grid.attach(value_label, 1, row, 1, 1)
            row += 1
            
            label = Gtk.Label()
            label.set_property("xalign", 0)
            label.set_markup("<b>文件大小:</b>")
            grid.attach(label, 0, row, 1, 1)
            
            value_label = Gtk.Label(label=file_size_str)
            value_label.set_property("xalign", 0)
            grid.attach(value_label, 1, row, 1, 1)
            row += 1
            
            close_button = Gtk.Button(label="关闭")
            close_button.connect("clicked", lambda w: prop_window.destroy())
            vbox.pack_start(close_button, False, False, 0)
            
            prop_window.show_all()
    
    def on_treeview_button_press(self, treeview, event):
        """处理树视图点击"""
        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
            try:
                path, column, x, y = treeview.get_path_at_pos(int(event.x), int(event.y))
                self.selected_path = path
                treeview.get_selection().select_path(path)
                self.menu.popup_at_pointer(event)
                return True
            except TypeError:
                pass
        return False
    
    def on_mark_as_game(self, widget):
        """标记为游戏"""
        if self.selected_path:
            iter_ = self.game_liststore.get_iter(self.selected_path)
            rom_name = self.game_liststore.get_value(iter_, 0)
            self.game_liststore.set_value(iter_, 2, self.TAG_GAME)
            self.game_tags[rom_name] = self.TAG_GAME
            self._save_game_tags()
    
    def on_mark_as_bios(self, widget):
        """标记为BIOS"""
        if self.selected_path:
            iter_ = self.game_liststore.get_iter(self.selected_path)
            rom_name = self.game_liststore.get_value(iter_, 0)
            self.game_liststore.set_value(iter_, 2, self.TAG_BIOS)
            self.game_tags[rom_name] = self.TAG_BIOS
            self._save_game_tags()
    
    def on_mark_as_unplayable(self, widget):
        """标记为不可玩"""
        if self.selected_path:
            iter_ = self.game_liststore.get_iter(self.selected_path)
            rom_name = self.game_liststore.get_value(iter_, 0)
            self.game_liststore.set_value(iter_, 2, self.TAG_UNPLAYABLE)
            self.game_tags[rom_name] = self.TAG_UNPLAYABLE
            self._save_game_tags()
    
    def on_clear_tag(self, widget):
        """清除标记"""
        if self.selected_path:
            iter_ = self.game_liststore.get_iter(self.selected_path)
            rom_name = self.game_liststore.get_value(iter_, 0)
            self.game_liststore.set_value(iter_, 2, self.TAG_NONE)
            if rom_name in self.game_tags:
                del self.game_tags[rom_name]
                self._save_game_tags()
    
    def create_recent_list(self):
        """创建最近列表"""
        self.recent_scrolled_window = Gtk.ScrolledWindow()
        self.recent_scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        
        self.recent_liststore = Gtk.ListStore(str, str, str)
        
        self.recent_treeview = Gtk.TreeView(model=self.recent_liststore)
        self.recent_treeview.connect("row-activated", self.on_recent_row_activated)
        
        renderer_name = Gtk.CellRendererText()
        column_name = Gtk.TreeViewColumn("游戏名称", renderer_name, text=1)
        column_name.set_sort_column_id(1)
        self.recent_treeview.append_column(column_name)
        
        renderer_time = Gtk.CellRendererText()
        column_time = Gtk.TreeViewColumn("游玩时间", renderer_time, text=2)
        column_time.set_sort_column_id(2)
        self.recent_treeview.append_column(column_time)
        
        self.recent_scrolled_window.add(self.recent_treeview)
    
    def refresh_rom_list(self, widget=None):
        """刷新游戏列表"""
        if not hasattr(self, 'game_liststore') or self.game_liststore is None:
            return
            
        self.game_liststore.clear()
        
        rom_files = glob.glob(os.path.join(self.rom_path, "*.zip"))
        game_entries = []
        
        for rom_file in rom_files:
            rom_basename = os.path.basename(rom_file)
            rom_name = os.path.splitext(rom_basename)[0]
            
            display_rom_name = self._truncate_zip_name(rom_name)
            tag_type = self.game_tags.get(rom_name, self.TAG_NONE)
            
            if not self.settings.get("show_bios_in_list", True) and tag_type == self.TAG_BIOS:
                continue
            
            if not self.settings.get("show_unplayable_in_list", True) and tag_type == self.TAG_UNPLAYABLE:
                continue
            
            display_name = self.custom_names.get(rom_name, 
                           self.game_aliases.get(rom_name, rom_basename))
            
            game_entries.append((rom_name, display_name, tag_type, display_rom_name))
        
        game_entries.sort(key=lambda x: x[1].lower())
        
        for entry in game_entries:
            self.game_liststore.append(entry)
        
        count = len(game_entries)
        self.statusbar.push(self.status_context, f"共 {count} 个游戏")
    
    def refresh_recent_list(self):
        """刷新最近列表"""
        self.recent_liststore.clear()
        
        for entry in self.recent_games:
            rom_name = entry["rom_name"]
            display_name = self.custom_names.get(rom_name, entry["game_name"])
            self.recent_liststore.append([
                rom_name,
                display_name,
                entry["played_at"]
            ])
    
    def run_game(self, rom_name):
        """运行游戏"""
        if self.game_tags.get(rom_name, self.TAG_NONE) == self.TAG_UNPLAYABLE:
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.WARNING,
                buttons=Gtk.ButtonsType.YES_NO,
                text=f"此游戏被标记为不可玩，确定要运行吗？"
            )
            response = dialog.run()
            dialog.destroy()
            
            if response != Gtk.ResponseType.YES:
                return
        
        rom_file = os.path.join(self.rom_path, f"{rom_name}.zip")
        
        if not os.path.exists(rom_file):
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text=f"找不到ROM文件: {rom_name}.zip"
            )
            dialog.run()
            dialog.destroy()
            return
        
        if not os.path.exists(self.advmame_path):
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text=f"找不到advmame可执行文件: {self.advmame_path}"
            )
            dialog.run()
            dialog.destroy()
            return
        
        try:
            subprocess.Popen([self.advmame_path, rom_name])
            self._save_recent_game(rom_name)
            self.refresh_recent_list()
        except Exception as e:
            dialog = Gtk.MessageDialog(
                parent=self.window,
                modal=True,
                destroy_with_parent=True,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text=f"运行游戏时出错: {str(e)}"
            )
            dialog.run()
            dialog.destroy()
    
    def on_game_row_activated(self, treeview, path, column):
        """激活游戏行"""
        model = treeview.get_model()
        rom_name = model[path][0]
        self.run_game(rom_name)
    
    def on_recent_row_activated(self, treeview, path, column):
        """激活最近游戏行"""
        model = treeview.get_model()
        rom_name = model[path][0]
        self.run_game(rom_name)
    
    def main(self):
        """主函数"""
        Gtk.main()

if __name__ == "__main__":
    frontend = AdvanceMAMEFrontend()
    frontend.main()
